package cn.wss.flowable.service.impl;

import cn.wss.flowable.common.Page;
import cn.wss.flowable.common.Params;
import cn.wss.flowable.model.FlowableProcessInstance;
import cn.wss.flowable.model.FlowableTask;
import cn.wss.flowable.service.Conditions;
import cn.wss.flowable.service.FlowableHistoryService;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.util.IoUtil;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.flowable.variable.api.history.HistoricVariableInstanceQuery;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class FlowableHistoryServiceImpl implements FlowableHistoryService, Conditions {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private TaskService taskService;

    /**
     * @Description 查看流程实例图片
     * @Param processInstanceId 流程实例id
     * @Return byte[]
     */
    @Override
    public byte[] getProcessPicture(String processInstanceId) {
        HistoricProcessInstance hp = getHisInsQuery().processInstanceId(processInstanceId).singleResult();
        if(hp == null){
            throw new RuntimeException("流程实例ID为 " + processInstanceId + " 的流程未找到");
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(hp.getProcessDefinitionId());
        ProcessEngineConfiguration processEngineConfiguration = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator generator = processEngineConfiguration.getProcessDiagramGenerator();

        List<String> activities = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        List<HistoricActivityInstance> instances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();

        for (HistoricActivityInstance item : instances) {
            if("sequenceFlow".equals(item.getActivityType())) {
                flows.add(item.getActivityId());
            } else {
                activities.add(item.getActivityId());
            }
        }
        InputStream inputStream = generator.generateDiagram(bpmnModel, "png", activities, flows, 1.0d, true);
        return IoUtil.readInputStream(inputStream, "flow diagram inputStream");
    }

    @Override
    public Page<FlowableProcessInstance> pageInstance(Map<String, Object> variables) {
        Page<FlowableProcessInstance> page = new Page(variables);
        Params params = new Params(variables);
        HistoricProcessInstanceQuery query = getHisInsQuery();

        // 流程实例id
        if (params.containsKey(PROCESS_INSTANCE_ID)) {
            query.processInstanceId(params.get(PROCESS_INSTANCE_ID));
        }
        // 启动人
        if (params.containsKey(STARTED_USER)) {
            query.startedBy(params.get(STARTED_USER));
        }
        // 参与流程处理的组
        if(params.containsKey(INVOLVED_GROUPS)){
            query.involvedGroups(params.getSet(STARTED_USER));
        }
        // 参与流程处理的人
        if(params.containsKey(INVOLVED_USER)){
            query.involvedUser(params.get(INVOLVED_USER));
        }
        // 业务key
        if(params.containsKey(BUSINESS_KEY)){
            query.processInstanceBusinessKey(params.get(BUSINESS_KEY));
        }
        // 是否结束
        if (params.containsKey(FINISH)) {
            if (params.getBoolean(FINISH)) {
                query.finished();
            } else {
                query.unfinished();
            }
        }
        //根据部署id过滤
        if(params.containsKey(PROCESS_DEFINITION_ID)) {
            query.processDefinitionId(params.get(PROCESS_DEFINITION_ID));
        }
        //根据部署key过滤
        if(params.containsKey(PROCESS_DEFINITION_KEY)) {
            query.processDefinitionKey(params.get(PROCESS_DEFINITION_KEY));
        }
        // 添加排序类型
        if (params.containsKey(SORT)) {
            if ("asc".equals(params.get(SORT))) {
                query.asc();
            } else {
                query.desc();
            }
        }
        // 添加排序条件 start:end
        if (params.containsKey(SORT_TYPE)) {
            if ("start".equals(params.get(SORT_TYPE))) {
                query.orderByProcessInstanceStartTime();
            } else {
                query.orderByProcessInstanceEndTime();
            }
        }

        // 流程变量查询
        if (params.containsKey("variables")) {
            List<Map<String, Object>> list = params.getList("variables");
            if (list != null && !list.isEmpty()) {
                for (Map<String, Object> data: list) {
                    String type = (String)variables.get("variableType");
                    if (StringUtils.isBlank(type)) {
                        continue;
                    }
                    switch (type) {
                        case "equals":
                            query.variableValueEquals((String)data.get("variableName"), data.get("variableValue"));
                        case "notEquals":
                            query.variableValueNotEquals((String)data.get("variableName"), data.get("variableValue"));
                        case "greaterThan":
                            query.variableValueGreaterThan((String)data.get("variableName"), data.get("variableValue"));
                        case "greaterThanOrEqual":
                            query.variableValueGreaterThanOrEqual((String)data.get("variableName"), data.get("variableValue"));
                        case "lessThan":
                            query.variableValueLessThan((String)data.get("variableName"), data.get("variableValue"));
                        case "lessThanOrEqual":
                            query.variableValueLessThanOrEqual((String)data.get("variableName"), data.get("variableValue"));
                        default:
                            query.variableValueLike((String)data.get("variableName"), "%" + (String) data.get("variableValue") + "%");
                    }
                }
            }
        }

        int totalElements = query.list().size();
        List<HistoricProcessInstance> data = query.listPage((page.getPageNum() - 1) * page.getPageSize(), page.getPageSize());
        List<FlowableProcessInstance> instances = new ArrayList<>();
        for (HistoricProcessInstance item: data) {
            FlowableProcessInstance instance = new FlowableProcessInstance();
            BeanUtils.copyProperties(item, instance);
            instances.add(instance);
        }

        page.setData(instances);
        page.setTotalElements(totalElements);
        return page;
    }

    @Override
    public Page<FlowableTask> pageTask(Map<String, Object> variables) {
        Page<FlowableTask> page = new Page(variables);
        Params params = new Params(variables);
        HistoricTaskInstanceQuery query = getHisTaskQuery();
        if (params.containsKey(PROCESS_INSTANCE_ID)) {
            query.processInstanceId(params.get(PROCESS_INSTANCE_ID));
        }
        if (params.containsKey(FINISH)) {
            if (params.getBoolean(FINISH)) {
                query.finished();
            } else {
                query.unfinished();
            }
        }
        if (params.containsKey(ASSIGNEE)) {
            query.taskAssignee(params.get(ASSIGNEE));
        }
        if (params.containsKey(ID)) {
            query.taskId(params.get(ID));
        }
        if (params.containsKey(BUSINESS_KEY)) {
            query.processInstanceBusinessKey(params.get(BUSINESS_KEY));
        }
        if (params.containsKey(PROCESS_FINISH)) {
            if (params.getBoolean(PROCESS_FINISH)) {
                query.processFinished();
            } else {
                query.processUnfinished();
            }
        }
        if (params.containsKey(SORT_TYPE)) {
            if ("start".equals(params.get(SORT_TYPE))) {
                query.orderByHistoricTaskInstanceStartTime();
            } else {
                query.orderByHistoricTaskInstanceEndTime();
            }
        }
        if (params.containsKey(SORT)) {
            if ("asc".equals(params.get(SORT))) {
                query.asc();
            } else {
                query.desc();
            }
        }

        // 流程变量查询
        if (params.containsKey("variables")) {
            List<Map<String, Object>> list = params.getList("variables");
            if (list != null && !list.isEmpty()) {
                for (Map<String, Object> data: list) {
                    String type = (String)data.get("variableType");
                    if (StringUtils.isBlank(type)) {
                        continue;
                    }
                    switch (type) {
                        case "equals":
                            query.processVariableValueEquals((String)data.get("variableName"), data.get("variableValue"));
                            break;
                        case "notEquals":
                            query.processVariableValueNotEquals((String)data.get("variableName"), data.get("variableValue"));
                            break;
                        case "greaterThan":
                            query.processVariableValueGreaterThan((String)data.get("variableName"), data.get("variableValue"));
                            break;
                        case "greaterThanOrEqual":
                            query.processVariableValueGreaterThanOrEqual((String)data.get("variableName"), data.get("variableValue"));
                            break;
                        case "lessThan":
                            query.processVariableValueLessThan((String)data.get("variableName"), data.get("variableValue"));
                            break;
                        case "lessThanOrEqual":
                            query.processVariableValueLessThanOrEqual((String)data.get("variableName"), data.get("variableValue"));
                            break;
                        default:
                            query.processVariableValueLike((String)data.get("variableName"), "%" + data.get("variableValue") + "%");
                            break;
                    }
                }
            }
        }

        int totalElements = query.list().size();
        List<HistoricTaskInstance> data = query.listPage((page.getPageNum() - 1) * page.getPageSize(), page.getPageSize());
        List<FlowableTask> tasks = new ArrayList<>();
        for (HistoricTaskInstance item: data) {
            FlowableTask task = new FlowableTask();
            BeanUtils.copyProperties(item, task);
            FlowableProcessInstance processInstance = instance(item.getProcessInstanceId());

            task.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
            task.setProcessDefinitionName(processInstance.getProcessDefinitionName());
            task.setComments(taskService.getTaskComments(task.getId()));

            task.setVariable(getVariables(item.getProcessInstanceId()));
            task.setLocalVariable(getLocalVariables(item.getId()));
            tasks.add(task);
        }
        page.setData(tasks);
        page.setTotalElements(totalElements);
        return page;
    }

    @Override
    public void deleteInstanceById(String id) {
        HistoricProcessInstance instance = getHisInsQuery().processInstanceId(id).singleResult();
        if (instance == null) {
            throw new RuntimeException("未查询到任务实例，请传递正确的实例id");
        }
        if (instance.getEndTime() == null) {
            throw new RuntimeException("当前实例未结束，不可删除");
        }
        historyService.deleteHistoricProcessInstance(id);
    }

    // 查询流程实例
    @Override
    public FlowableProcessInstance instance(String id) {
        HistoricProcessInstance instance = getHisInsQuery().processInstanceId(id).singleResult();
        FlowableProcessInstance flowableProcessInstance = new FlowableProcessInstance();
        BeanUtils.copyProperties(instance, flowableProcessInstance);
        return flowableProcessInstance;
    }

    // 查询流程变量
    @Override
    public Map<String, Object> getVariables(String processInstanceId) {
        List<HistoricVariableInstance> data = getHisVariableQuery().processInstanceId(processInstanceId).list();
        Map<String, Object> variables = new HashMap<>();
        for (HistoricVariableInstance item: data) {
            String key = item.getVariableName();
            if ("taskListener".equals(key) || "executionListener".equals(key)) {
                continue;
            }
            variables.put(key, item.getValue());
        }
        return variables;
    }

    // 查询局部变量
    @Override
    public Map<String, Object> getLocalVariables(String taskId) {
        List<HistoricVariableInstance> data = getHisVariableQuery().taskId(taskId).list();
        Map<String, Object> variables = new HashMap<>();
        for (HistoricVariableInstance item: data) {
            variables.put(item.getVariableName(), item.getValue());
        }
        return variables;
    }

    public HistoricProcessInstanceQuery getHisInsQuery(){
        return historyService.createHistoricProcessInstanceQuery();
    }

    public HistoricTaskInstanceQuery getHisTaskQuery(){
        return historyService.createHistoricTaskInstanceQuery();
    }

    public HistoricVariableInstanceQuery getHisVariableQuery() {
        return historyService.createHistoricVariableInstanceQuery();
    }
}
